﻿// This File is part of Innovation Foundry's "QualityHelper Project"
//
// Copyright  2009 Innovation Foundry Pty Ltd
// All rights reserved
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the
// Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA  02110-1301 USA
// or alternatively it may be downloaded from <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using InnovationFoundry.QualityHelper.Silverlight.BLL.Objects;
using InnovationFoundry.QualityHelper.Silverlight.Controls.Zoom;
using InnovationFoundry.QualityHelper.Silverlight.Assets.Diagrams;
using InnovationFoundry.QualityHelper.Silverlight.Utilities;
using System.Windows.Markup;
using InnovationFoundry.QualityHelper.Silverlight.Assets.Resources;

namespace InnovationFoundry.QualityHelper.Silverlight.Controls.Diagram
{
    public partial class DiagramControl : UserControl
    {
        private DiagramObject diagramToDraw;
        private DiagramControl diagramReflection = null;
        private Zoomify zoom = new Zoomify();
        private List<ProcSeperator> proch = new List<ProcSeperator>();
        List<String> groups = new List<string>();
        public delegate void ProcedureClickedHandler(object sender, ProcedureClickedArgs e);
        public event ProcedureClickedHandler ProcedureClicked;
        public delegate void ProcedureDoubleClickedHandler(object sender, ProcedureClickedArgs e);
        public event ProcedureDoubleClickedHandler ProcedureDoubleClicked;
        private bool isConnsDrawnOnce = true;
        private const string PREFIX = "*++";
        private const string SUFFIX = "++*";
        private const double ITEMSIDE = 125;
        private const double LEFTOFFSET = 15;
        private const double TOPOFFSET = 25;

        /// <summary>
        /// Occurs when a procedure has been clicked. 
        /// </summary>

        public DiagramControl()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(DiagramControl_Loaded);
            this.LayoutRoot.Children.Add(zoom);
            this.LayoutUpdated += new EventHandler(DiagramControl_LayoutUpdated);
        }

        void DiagramControl_LayoutUpdated(object sender, EventArgs e)
        {
            if (!isConnsDrawnOnce)
            {
                isConnsDrawnOnce = true;

                DrawConnections();
            }

        }

        public DiagramControl DiagramReflection
        {
            get { return diagramReflection; }
            set { diagramReflection = value; }
        }
        
        public DiagramObject DiagramToDraw
        {
            get { return diagramToDraw; }
            set { diagramToDraw = value; }
        }

        public void MoveOutOfFocus()
        {
            zoom.ResetScale();
            zoom.ContentToZoom = null;
            zoom.ReflectionToZoom = null;
            ReleaseMouseCapture();
            //this.LayoutRoot.Children.Remove(zoom);
            //foreach (UIElement c in this.StackMain.Children)
            //{
            //    if (c.GetType() == typeof(Canvas))
            //    {
            //        Canvas can = c as Canvas;
            //        foreach (UIElement ctl in can.Children)
            //        {
            //            if (ctl.GetType() == typeof(Button))
            //                ((Button)ctl).IsEnabled = false;

            //        }
            //    }
            //}
        }

        public void MoveInFocus()
        {
            zoom.ContentToZoom = this;
            zoom.ReflectionToZoom = this.DiagramReflection;
            //this.LayoutRoot.Children.Add(zoom);
            //foreach (UIElement c in this.StackMain.Children)
            //{
            //    if (c.GetType()==typeof(Button))
            //        ((Button)c).IsEnabled = true;
            //}
        }

        void DiagramControl_Loaded(object sender, RoutedEventArgs e)
        {
            //First Draw the Users
            DrawUsers();
            //Next Draw the Procedures in StackPanel of Users (Horizontal)
            DrawProcedures();
            ArrangeItems();
            isConnsDrawnOnce = false;
            //Next Draw Connections
            UpdateLayout();
            //Moved to an event
            //System.Threading.Thread.Sleep(175);
            //DrawConnections();
        }

        private void DrawUsers()
        {
            //generate groups
            foreach (ProcedureObject proc in DiagramToDraw.Procedures)
            {
                foreach (GroupObject group in proc.Groups)
                {
                    if (!groups.Contains(group.GroupName))
                    {
                        if (proc.Purpose.PurposeName.ToLower() == "start")
                            groups.Insert(0, group.GroupName);
                        else
                            groups.Add(group.GroupName);
                    }
                }
            }

            double perHeight = ITEMSIDE; // (Height - groups.Count * 20) / groups.Count;


            Color mainColor = ColorHelper.GetColor("#66888888"); //Color.FromArgb(TOPOFFSET5, 128, 128, 200);
            Color altColor = ColorHelper.GetColor("#66A7A37E"); //Color.FromArgb(TOPOFFSET5,200,128,128);
            int i = 0;
            foreach (string group in groups)
            {
                Canvas pnl = new Canvas();
                pnl.Name = DiagramToDraw.DiagramID + group;
                if (i % 2 > 0)
                    pnl.Background = new SolidColorBrush(altColor);
                else
                    pnl.Background = new SolidColorBrush(mainColor);

                pnl.Height = perHeight;
                //pnl.HorizontalAlignment = HorizontalAlignment.Stretch;
                pnl.Width = Width;

                TextBlock blk = new TextBlock();
                blk.Text = group;
                blk.Margin = new Thickness(5,0,0,0);
                blk.Foreground = new SolidColorBrush(Colors.Black);
                StackMain.Children.Add(blk);
                StackMain.Children.Add(pnl);

                ProcSeperator psep = new ProcSeperator();
                psep.currentLeft = LEFTOFFSET;
                psep.currentTop = TOPOFFSET;
                psep.GroupName = group;
                psep.usedrow = 0;
                proch.Add(psep);
                i++;
            }
        }

        private void DrawProcedures()
        {
            //canvas to draw
            Canvas procedure = new Canvas();

            foreach (ProcedureObject p in DiagramToDraw.Procedures)
            {
                if (p.Purpose.FlowchartElement == "Diamond")
                {
                    procedure = XamlReader.Load(ControlResources.Diamond) as Canvas;
                    procedure.Name = DiagramToDraw.DiagramID + p.ProcedureID;
                    procedure.Width = 75;
                    procedure.Height = 55;
                    procedure.Tag = "diamond";
                }
                else if (p.Purpose.FlowchartElement == "Rectangle" || p.Purpose.FlowchartElement == "BoxSquare")
                {
                    procedure = XamlReader.Load(ControlResources.Square) as Canvas;
                    procedure.Name = DiagramToDraw.DiagramID + p.ProcedureID;
                    procedure.Width = 100;
                    procedure.Height = 55;
                    procedure.Tag = "square";
                }
                else if (p.Purpose.FlowchartElement == "RRectangle" || p.Purpose.FlowchartElement == "BoxRound")
                {
                    procedure = XamlReader.Load(ControlResources.RoundSquare) as Canvas;
                    procedure.Name = DiagramToDraw + p.ProcedureID;
                    procedure.Tag = "rsquare";
                    procedure.Width = 100;
                    procedure.Height = 55;
                }
                //set name
                procedure.Name = p.ProcedureID;
                //add tooltip
                Button btn = procedure.FindName("btn") as Button;
                if (btn != null)
                {
                    TextBlock tb = new TextBlock();
                    tb.Text = p.ProcedureDescription;
                    tb.TextWrapping = TextWrapping.Wrap;
                    tb.MaxWidth = 120;

                    ToolTip t = new ToolTip();
                    t.Opacity = 0.70;
                    t.Content = tb;
                    ToolTipService.SetToolTip(btn, t);
                    //ToolTipService.SetToolTip(btn, string.Concat(p.ProcedureDescription, ""));
                    //ToolTipService.ToolTipProperty;
                    btn.Click += new RoutedEventHandler(procedure_Click);
                    //btn.MouseEnter += new MouseEventHandler(btn_MouseEnter);
                    //btn.MouseLeave += new MouseEventHandler(btn_MouseLeave);
                    btn.Click += new RoutedEventHandler(btn_Click);
                }
                //add events (uses button now)
                //procedure.MouseLeftButtonUp += new MouseButtonEventHandler(procedure_MouseLeftButtonUp);
                //set proc name
                TextBlock tbName = procedure.FindName("txtName") as TextBlock;
                string[] pNameArray = p.ProcedureName.Split();
                if (pNameArray.Length > 0)
                    tbName.Text = pNameArray[0];
                if (pNameArray.Length > 1)
                    tbName.Text += "\n" + pNameArray[1];
                if (pNameArray.Length > 2)
                    tbName.Text += "...";
                tbName.Text += (p.InboundReferences.Count > 0 || p.OutboundReferences.Count > 0) ? "\n" : null;
                tbName.Text += (p.InboundReferences.Count > 0) ? "^" : null;
                tbName.Text += (p.OutboundReferences.Count > 0) ? "+" : null;

                //find group and add to it
                foreach (GroupObject group in p.Groups)
                    (StackMain.FindName(DiagramToDraw.DiagramID + group.GroupName) as Canvas).Children.Add(procedure);
                


                //TODO find a way to move procs down a row. grid maybe?

                //Canvas procedureGroup = new Canvas();
                //foreach (GroupObject group in p.Groups)
                //    procedureGroup = (StackMain.FindName(group.GroupName) as Canvas);
                
                //if (procedureGroup != null) 
                //{
                //    if (procedureGroup.Children.Count > 3)
                //        procedure.SetValue(Canvas.TopProperty, double.Parse(procedure.GetValue(Canvas.TopProperty).ToString()) + 30);
                //}
                //    procedureGroup.Children.Add(procedure);
            }
        }

        void btn_Click(object sender, RoutedEventArgs e)
        {
            Storyboard sb = new Storyboard();
            Duration sbd = new Duration(TimeSpan.FromSeconds(0.3));
            sb.Duration = sbd;

            Button btn = sender as Button;
            if (btn == null)
                return;
            Canvas procCanvas = btn.Parent as Canvas;
            ProcedureObject proc = DiagramToDraw.Procedures.First(p => p.ID.ToString() == procCanvas.Name);
            var connections = (from cxns in DiagramToDraw.Connections
                               where (cxns.ProcedureOrigin.ID.ToString() == proc.ID.ToString() || cxns.ProcedureDestination.ID.ToString() == proc.ID.ToString())
                               select cxns);
            if (connections.Count() < 1)
                return;
            foreach (FrameworkElement fui in LayoutRoot.Children)
            {
                if (fui != StackMain)
                {
                    if (fui.Name.IndexOf(PREFIX + proc.ID.ToString() + SUFFIX) < 0)
                    {
                        DoubleAnimation da = new DoubleAnimation();
                        da.Duration = sbd;
                        da.From = fui.Opacity;
                        da.To = 0.1;
                        Storyboard.SetTarget(da, fui);
                        Storyboard.SetTargetProperty(da, new PropertyPath("(Canvas.Opacity)"));
                        sb.Children.Add(da);
                        //fui.OpacityMask = new SolidColorBrush(Color.FromArgb(40, TOPOFFSET5, TOPOFFSET5, TOPOFFSET5));
                        //fui.Visibility = Visibility.Collapsed;
                    }
                    else
                    {
                        DoubleAnimation da = new DoubleAnimation();
                        da.Duration = sbd;
                        da.From = fui.Opacity;
                        da.To = 1;
                        Storyboard.SetTarget(da, fui);
                        Storyboard.SetTargetProperty(da, new PropertyPath("(Canvas.Opacity)"));
                        sb.Children.Add(da);
                    }
                }
                else
                {
                    //hide all the procs not involved with this proc
                    foreach (object children in StackMain.Children)
                    {
                        Canvas grp = children as Canvas;
                        if (grp == null)
                            continue;
                        foreach (Canvas pCanvas in grp.Children)
                        {
                            if (!connections.Any(p => p.ProcedureDestination.ID.ToString() == pCanvas.Name || p.ProcedureOrigin.ID.ToString() == pCanvas.Name))
                            {
                                DoubleAnimation da = new DoubleAnimation();
                                da.Duration = sbd;
                                da.From = pCanvas.Opacity;
                                da.To = 0.2;
                                Storyboard.SetTarget(da, pCanvas);
                                Storyboard.SetTargetProperty(da, new PropertyPath("(Canvas.Opacity)"));
                                sb.Children.Add(da);
                                //pCanvas.OpacityMask = new SolidColorBrush(Color.FromArgb(40, TOPOFFSET5, TOPOFFSET5, TOPOFFSET5));
                                //pCanvas.Visibility = Visibility.Collapsed;
                            }
                            else
                            {
                                DoubleAnimation da = new DoubleAnimation();
                                da.Duration = sbd;
                                da.From = pCanvas.Opacity;
                                da.To = 1;
                                Storyboard.SetTarget(da, pCanvas);
                                Storyboard.SetTargetProperty(da, new PropertyPath("(Canvas.Opacity)"));
                                sb.Children.Add(da);
                            }
                        }
                    }
                }

            }
            //LayoutRoot.Resources.Add(Guid.NewGuid().ToString(), sb);
            sb.Begin();
        }

       

        private DateTime lastProcTimeClicked = DateTime.Now;
        private string lastProcStringClicked = null;
        void procedure_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) {
            //throw new NotImplementedException();
            Canvas c = sender as Canvas;
            if (c == null)
                return;
            TriggerProcedureClick(c);
        }

        void procedure_Click(object sender, RoutedEventArgs e)
        {
            Button btn = sender as Button;
            if (btn == null)
                return;
            Canvas c = btn.Parent as Canvas;
            if (c == null)
                return;
            TriggerProcedureClick(c);
        }

        void TriggerProcedureClick(Canvas c)
        {
            ProcedureObject proc = findProcedure(c.Name);

            if (ProcedureDoubleClicked != null && lastProcTimeClicked.AddSeconds(1.5).CompareTo(DateTime.Now) > 0 && lastProcStringClicked == proc.ProcedureID)
            {
                ProcedureClickedArgs procArgs = new ProcedureClickedArgs(DiagramToDraw, proc);
                ProcedureDoubleClicked(this, procArgs);
                lastProcStringClicked = null;
            }
            else if (ProcedureClicked != null)
            {
                ProcedureClickedArgs procArgs = new ProcedureClickedArgs(DiagramToDraw, proc);
                ProcedureClicked(this, procArgs);
                lastProcStringClicked = proc.ProcedureID;
                lastProcTimeClicked = DateTime.Now;
            }
        }

        private ProcedureObject findProcedure(string ProcID) {
            foreach (ProcedureObject p in DiagramToDraw.Procedures) {
                if (p.ProcedureID == ProcID)
                    return p;
            }
            return null;
        }

        private void ArrangeItems()
        {
            int heightsIncreased = 0;
            double heightGroups = 0.0;
            for (int j = 0; j < groups.Count; j++)
            {
                //Iterate Each Item
                Canvas grpcanvas = StackMain.FindName(DiagramToDraw.DiagramID + groups[j]) as Canvas;
                ProcSeperator pset = (from p in proch
                                      where p.GroupName == groups[j]
                                      select p).First();
                double currentRowWidth = LEFTOFFSET;
                for (int i = 0; i < grpcanvas.Children.Count; i++)
                {
                    Canvas diagramItem = grpcanvas.Children[i] as Canvas;
                    double rowCapacity = Math.Floor(this.Width / (ITEMSIDE + 25));
                    rowCapacity = (rowCapacity == 0.0) ? 1.0 : rowCapacity;
                    currentRowWidth += diagramItem.Width + 25;
                    if (currentRowWidth + diagramItem.Width <= this.Width)
                    {
                        pset.usedrow++;
                    }
                    else
                    {
                        currentRowWidth = 0;
                        pset.usedrow = 0;
                        pset.currentTop += ITEMSIDE;
                        grpcanvas.Height += ITEMSIDE;
                        this.Height += ITEMSIDE;
                        pset.currentLeft = LEFTOFFSET;
                        heightsIncreased++;
                    }
                    diagramItem.SetValue(Canvas.LeftProperty, pset.currentLeft);
                    diagramItem.SetValue(Canvas.TopProperty, pset.currentTop);
                    //Increment value
                    pset.currentLeft += ITEMSIDE;

                }
                heightGroups += TOPOFFSET + grpcanvas.Height;
                if (j == groups.Count - 1 && heightGroups < this.Height)
                {
                    grpcanvas.Height += this.Height - heightGroups + TOPOFFSET;
                }                
            }

            //if (heightsIncreased > 0)
            //{
            //    //Decrease heights for items with least amount of procedures
            //    List<HeightRef> listofcanvases = new List<HeightRef>();
            //    foreach (UIElement elem in StackMain.Children)
            //    {
            //        if (elem is Canvas)
            //        {
            //            HeightRef r = new HeightRef();
            //            r.Count = (elem as Canvas).Children.Count;
            //            r.Name = (elem as Canvas).Name;
            //            listofcanvases.Add(r);
            //        }
            //    }


            //    //Select with Least
            //    var leastc = from o in listofcanvases
            //                 orderby o.Count
            //                 select o;
            //    var leastsel = leastc.Take(heightsIncreased);
            //    foreach (HeightRef hr in leastsel)
            //    {
            //        (StackMain.FindName(hr.Name) as Canvas).Height -= 80;
            //    }
                              
                             
            //}
        }


        private void DrawConnections()
        {
            //If Source is Right to Origin
            //If Source is Left to Origin
            //If Source is Top to Origin
            //If Source is Bottom to Origina
            double randomOffset = 0;
            foreach (ConnectionObject conn in diagramToDraw.Connections)
            {
                randomOffset += 3;
                randomOffset = (randomOffset + 3) % 20;
                double randomOffsetFixed = randomOffset - 10;


                ProcedureObject psrc = (from p in DiagramToDraw.Procedures where p.ProcedureID == conn.ProcedureOrigin.ProcedureID select p).FirstOrDefault();
                ProcedureObject pdest = (from p in DiagramToDraw.Procedures where p.ProcedureID == conn.ProcedureDestination.ProcedureID select p).FirstOrDefault();
                if (psrc == null || pdest == null)
                    continue;

                //Canvas grpAccesssrc = StackMain.FindName(DiagramToDraw.DiagramID + psrc.Group.GroupName) as Canvas;
                //Canvas grpAccessdest = StackMain.FindName(DiagramToDraw.DiagramID + pdest.Group.GroupName) as Canvas;
                //Canvas src = CrudeFindName(grpAccesssrc, DiagramToDraw.DiagramID + psrc.ProceedureID);
                //Canvas dest = CrudeFindName(grpAccessdest, DiagramToDraw.DiagramID + pdest.ProceedureID);

                List<GroupObject> groups = new List<GroupObject>();
                groups.AddRange(psrc.Groups);
                groups.AddRange(pdest.Groups);
                Dictionary<ProcedureObject, Canvas> procGroup = new Dictionary<ProcedureObject, Canvas>();
                Canvas tempGroupCanvas = null;

                Canvas src = CrudeFindName(groups, psrc.ProcedureID, out tempGroupCanvas);
                procGroup.Add(psrc, tempGroupCanvas);

                Canvas dest = CrudeFindName(groups, pdest.ProcedureID, out tempGroupCanvas);
                procGroup.Add(pdest, tempGroupCanvas);
                
                if (src == null || dest == null)
                    continue;//Move to next Item, Connection not valid;

                RelativeToSourceSide relativePosside = new RelativeToSourceSide();
                RelativeToSourceLevel relativePoslevel = new RelativeToSourceLevel();

                //Relative Placement to Overall Page
                Point offsetsrc = src.TransformToVisual(LayoutRoot).Transform(new Point(0, 0));
                Point offsetdest = dest.TransformToVisual(LayoutRoot).Transform(new Point(0, 0));

                #region "Determine Position"
                if (src != null && dest != null)
                {
                    relativePosside = RelativeToSourceSide.SameLine;
                    relativePoslevel = RelativeToSourceLevel.SameLevel;

                    if (offsetdest.X - offsetsrc.X > 5)
                        relativePosside = RelativeToSourceSide.Left;
                    else if (offsetsrc.X - offsetdest.X > 5)
                        relativePosside = RelativeToSourceSide.Right;

                    if (offsetdest.Y - offsetsrc.Y > 5)
                        relativePoslevel = RelativeToSourceLevel.Top;
                    else if (offsetsrc.Y - offsetdest.Y > 5)
                        relativePoslevel = RelativeToSourceLevel.Bottom;

                }
                #endregion

                //Drawing Logic

                Canvas result = null;
                if (relativePoslevel == RelativeToSourceLevel.Top)
                {
                    if (relativePosside == RelativeToSourceSide.Left)
                    {
                        //Source is top and left
                        #region "Top and Left"
                        result = XamlReader.Load(ControlResources.ArrowAngleRightDown.Replace("{color}", conn.ConnectionType.ColorRGBHex)) as Canvas;
                        ConnectorPoint cp = GetConnectorPoint(conn, offsetsrc, offsetdest);
                        Canvas.SetLeft(result, offsetsrc.X + (src.Width / 2) + randomOffsetFixed);
                        Canvas.SetTop(result, cp.SrcTop + (src.Height));
                        Rectangle line1 = ((Rectangle)result.FindName("line1ArrowAngleRightDown"));
                        Rectangle line2 = ((Rectangle)result.FindName("line2ArrowAngleRightDown"));
                        Rectangle line3 = ((Rectangle)result.FindName("line3ArrowAngleRightDown"));
                        line1.Height = (offsetdest.Y - (offsetsrc.Y + src.Height))/2 - randomOffsetFixed;
                        line2.Width = offsetdest.X - offsetsrc.X;
                        Canvas.SetLeft(line2, Canvas.GetLeft(line1) );
                        Canvas.SetTop(line2, Canvas.GetTop(line1) + line1.Height - 2);

                        double l3height = (offsetdest.Y - (offsetsrc.Y + src.Height)) / 2 + 1 + randomOffsetFixed;
                        if (l3height > 0)
                            line3.Height = l3height;
                        Canvas.SetLeft(line3, Canvas.GetLeft(line2) + line2.Width);
                        Canvas.SetTop(line3, Canvas.GetTop(line2));
                        ((Polygon)result.FindName("triangleArrowAngleRightDown")).SetValue(Canvas.LeftProperty, Canvas.GetLeft(line3) - 3);
                        ((Polygon)result.FindName("triangleArrowAngleRightDown")).SetValue(Canvas.TopProperty, Canvas.GetTop(line3) + line3.Height - 6);

                        if (conn.ConnectionType.HasConnectionType)
                        {
                            if (conn.ConnectionType.IsYesConnection)
                                ToolTipService.SetToolTip(result, "Yes");
                            else if (conn.ConnectionType.IsNoConnection)
                                ToolTipService.SetToolTip(result, "No");
                            else
                                ToolTipService.SetToolTip(result, conn.ConnectionType.ConnectionTypeName);
                        }
                        #endregion
                    }
                    else if (relativePosside == RelativeToSourceSide.Right)
                    {
                        //Source is top and Right
                        #region "Top and Right"
                        result = XamlReader.Load(ControlResources.ArrowAngleLeftDown.Replace("{color}", conn.ConnectionType.ColorRGBHex)) as Canvas;
                        ConnectorPoint cp = GetConnectorPoint(conn, offsetsrc, offsetdest);
                        Canvas.SetLeft(result, offsetsrc.X + (src.Width / 2));
                        Canvas.SetTop(result, cp.SrcTop + (src.Height));
                        Rectangle line1 = ((Rectangle)result.FindName("line1"));
                        Rectangle line2 = ((Rectangle)result.FindName("line2"));
                        Rectangle line3 = ((Rectangle)result.FindName("line3"));
                        line1.Height = (offsetdest.Y - offsetsrc.Y) / 2 - (2*dest.Height / 3) + 1 + randomOffsetFixed;
                        line2.Width = offsetsrc.X - offsetdest.X;
                        Canvas.SetLeft(line2, Canvas.GetLeft(line1) - line2.Width);
                        Canvas.SetTop(line2, Canvas.GetTop(line1) + line1.Height - 2);

                        double l3height = ((offsetdest.Y - offsetsrc.Y) / 2) - dest.Height/3 - 1 - randomOffsetFixed;
                        if (l3height > 0)
                            line3.Height = l3height;
                        Canvas.SetLeft(line3, Canvas.GetLeft(line2));
                        Canvas.SetTop(line3, Canvas.GetTop(line2));
                        ((Polygon)result.FindName("triangle")).SetValue(Canvas.LeftProperty, Canvas.GetLeft(line3) - 3);
                        ((Polygon)result.FindName("triangle")).SetValue(Canvas.TopProperty, Canvas.GetTop(line3) + line3.Height - 6);

                        if (conn.ConnectionType.HasConnectionType)
                        {
                            if (conn.ConnectionType.IsYesConnection)
                                ToolTipService.SetToolTip(result, "Yes");
                            else if (conn.ConnectionType.IsNoConnection)
                                ToolTipService.SetToolTip(result, "No");
                            else
                                ToolTipService.SetToolTip(result, conn.ConnectionType.ConnectionTypeName);
                        }
                        #endregion
                    }
                    else
                    {
                        //Source is at top and same align
                        #region Top and Same Align
                        result = XamlReader.Load(ControlResources.ArrowDown.Replace("{color}", conn.ConnectionType.ColorRGBHex)) as Canvas;
                        ConnectorPoint cp = GetConnectorPoint(conn, offsetsrc, offsetdest);
                        Canvas.SetLeft(result, cp.SrcLeft + (src.Width / 2) + randomOffsetFixed);
                        double lengthOfLine = offsetdest.Y - (offsetsrc.Y + dest.Height);
                        ((Rectangle)result.FindName("lineArrowDown")).Height = lengthOfLine;
                        ((Polygon)result.FindName("triangleArrowDown")).SetValue(Canvas.TopProperty, lengthOfLine - 7);
                        Canvas.SetTop(result, offsetsrc.Y + src.Height);
                        #endregion
                    }

                }
                else if (relativePoslevel == RelativeToSourceLevel.Bottom)
                {
                    //Source is at bottom
                    if (relativePosside == RelativeToSourceSide.Left)
                    {
                        //Source is bottom and left
                        #region Bottom and Left
		                result = XamlReader.Load(ControlResources.ArrowAngleRightTop.Replace("{color}", conn.ConnectionType.ColorRGBHex)) as Canvas;
                        ConnectorPoint cp = GetConnectorPoint(conn, offsetsrc, offsetdest);
                        Rectangle line1 = ((Rectangle)result.FindName("line1ArrowAngleRightTop"));
                        Rectangle line2 = ((Rectangle)result.FindName("line2ArrowAngleRightTop"));
                        Rectangle line3 = ((Rectangle)result.FindName("line3ArrowAngleRightTop"));
                        line1.Height = (offsetsrc.Y - offsetdest.Y) / 2 - dest.Height/2 + 1 +randomOffsetFixed;
                        Canvas.SetLeft(result, offsetsrc.X + (src.Width / 2) + randomOffsetFixed);
                        Canvas.SetTop(result, offsetsrc.Y - (line1.Height));
                        line2.Width = offsetdest.X - offsetsrc.X;
                        Canvas.SetLeft(line2, Canvas.GetLeft(line1));
                        Canvas.SetTop(line2, Canvas.GetTop(line1));

                        double l3height = ((offsetsrc.Y - offsetdest.Y) / 2) - dest.Height/2 - 1 -randomOffsetFixed;
                        if (l3height > 0)
                            line3.Height = l3height;
                        Canvas.SetLeft(line3, Canvas.GetLeft(line2) + line2.Width);
                        Canvas.SetTop(line3, Canvas.GetTop(line2) - line3.Height + 2);
                        ((Polygon)result.FindName("triangleArrowAngleRightTop")).SetValue(Canvas.LeftProperty, Canvas.GetLeft(line3) - 3);
                        ((Polygon)result.FindName("triangleArrowAngleRightTop")).SetValue(Canvas.TopProperty, Canvas.GetTop(line3) );

                        if (conn.ConnectionType.HasConnectionType)
                        {
                            if (conn.ConnectionType.IsYesConnection)
                                ToolTipService.SetToolTip(result, "Yes");
                            else if (conn.ConnectionType.IsNoConnection)
                                ToolTipService.SetToolTip(result, "No");
                            else
                                ToolTipService.SetToolTip(result, conn.ConnectionType.ConnectionTypeName);
                        }
                    	#endregion          
                    }
                    else if (relativePosside == RelativeToSourceSide.Right)
                    {
                        //Source is bottom and Right
                        #region Bottom and Right
                        result = XamlReader.Load(ControlResources.ArrowAngleLeftTop.Replace("{color}", conn.ConnectionType.ColorRGBHex)) as Canvas;
                        ConnectorPoint cp = GetConnectorPoint(conn, offsetsrc, offsetdest);
                        Rectangle line1 = ((Rectangle)result.FindName("line1ArrowAngleLeftTop"));
                        Rectangle line2 = ((Rectangle)result.FindName("line2ArrowAngleLeftTop"));
                        Rectangle line3 = ((Rectangle)result.FindName("line3ArrowAngleLeftTop"));
                        line1.Height = (offsetsrc.Y - offsetdest.Y) / 2 + 1 -randomOffsetFixed;
                        Canvas.SetLeft(result, offsetsrc.X + (src.Width / 2)+randomOffsetFixed);
                        Canvas.SetTop(result, offsetsrc.Y - line1.Height);
                        line2.Width = offsetsrc.X - offsetdest.X;
                        Canvas.SetLeft(line2, Canvas.GetLeft(line1) - line2.Width);
                        Canvas.SetTop(line2, Canvas.GetTop(line1));

                        double l3height = ((offsetsrc.Y - offsetdest.Y) / 2) - dest.Height - 1 + randomOffsetFixed;
                        if (l3height > 0)
                        line3.Height = l3height;
                        Canvas.SetLeft(line3, Canvas.GetLeft(line2));
                        Canvas.SetTop(line3, Canvas.GetTop(line2) - line3.Height + 2);
                        ((Polygon)result.FindName("triangleArrowAngleLeftTop")).SetValue(Canvas.LeftProperty, Canvas.GetLeft(line3) - 3);
                        ((Polygon)result.FindName("triangleArrowAngleLeftTop")).SetValue(Canvas.TopProperty, Canvas.GetTop(line3));

                        if (conn.ConnectionType.HasConnectionType)
                        {
                            if (conn.ConnectionType.IsYesConnection)
                                ToolTipService.SetToolTip(result, "Yes");
                            else if (conn.ConnectionType.IsNoConnection)
                                ToolTipService.SetToolTip(result, "No");
                            else
                                ToolTipService.SetToolTip(result, conn.ConnectionType.ConnectionTypeName);
                        }
                        #endregion
                    }
                    else
                    {
                        //Source is at botoom and same align
                        #region "Bottom and Same align"
                        result = XamlReader.Load(ControlResources.ArrowUp.Replace("{color}", conn.ConnectionType.ColorRGBHex)) as Canvas;
                        ConnectorPoint cp = GetConnectorPoint(conn, offsetsrc, offsetdest);
                        Canvas.SetLeft(result, cp.SrcLeft + (src.Width/2) + randomOffsetFixed);
                        double lengthOfLine = offsetsrc.Y - (offsetdest.Y+dest.Height)-2;
                        ((Rectangle)result.FindName("lineArrowUp")).Height = lengthOfLine;
                        //((Polygon)result.FindName("triangleArrowUp")).SetValue(Canvas.LeftProperty, lengthOfLine - 7.586);
                        Canvas.SetTop(result, offsetsrc.Y - lengthOfLine + 1);
                        #endregion
                    }
                }
                else
                {
                    //Source is at same level
                    if (relativePosside == RelativeToSourceSide.Left)
                    {
                        //Source is same level and left
                        #region "same level and left"
                        result = XamlReader.Load(ControlResources.ArrowRight.Replace("{color}", conn.ConnectionType.ColorRGBHex)) as Canvas;
                        ConnectorPoint cp = GetConnectorPoint(conn, offsetsrc, offsetdest);
                        Canvas.SetLeft(result, cp.SrcLeft + src.Width);
                        Canvas.SetTop(result, cp.SrcTop + (src.Height /2) + randomOffsetFixed);
                        double lengthOfLine = (Canvas.GetLeft(dest) - (Canvas.GetLeft(src)+src.Width));
                        ((Rectangle)result.FindName("line")).Width = lengthOfLine;
                        ((Polygon)result.FindName("triangle")).SetValue(Canvas.LeftProperty, lengthOfLine - 7.586);
                        #endregion
                    }
                    else if (relativePosside == RelativeToSourceSide.Right)
                    {
                        //Source is same level and Right
                        #region "Same level and right"
                        result = XamlReader.Load(ControlResources.ArrowLeft.Replace("{color}", conn.ConnectionType.ColorRGBHex)) as Canvas;
                        ConnectorPoint cp = GetConnectorPoint(conn, offsetsrc, offsetdest);
                        Canvas.SetLeft(result, (offsetdest.X + dest.Width) + 8);
                        Canvas.SetTop(result, cp.SrcTop + (src.Height / 2) + randomOffsetFixed);
                        double lengthOfLine = (Canvas.GetLeft(src) - (offsetdest.X + dest.Width));
                        ((Rectangle)result.FindName("lineArrowLeft")).Width = lengthOfLine;
                        ((Polygon)result.FindName("triangleArrowleft")).SetValue(Canvas.LeftProperty, 1d);
                        #endregion
                    }
                    else
                    {
                        //Source is at same level and same align
                        //Nothing needs to be done, we are not counting self-joins yet.
                    }
                }
                

                //Finally Add it to the Parent
                if (result != null)
                {
                    result.Name = PREFIX + psrc.ID.ToString() + SUFFIX + PREFIX + pdest.ID.ToString() + SUFFIX;
                    this.LayoutRoot.Children.Insert(0, result);
                }
            }

        }

        private double getActualLeft(string procID, Canvas groupCanvas) {
            double returnValue = 0;
            foreach (UIElement el in StackMain.Children) {
                if (el is Canvas) {
                    string blarg = (el as Canvas).Name;
                    
                    returnValue = double.Parse(el.GetValue(Canvas.TopProperty).ToString());
                    returnValue = double.Parse(el.GetValue(Canvas.LeftProperty).ToString());
                    double left = Canvas.GetLeft(el);
                    double top = Canvas.GetTop(el);


                } else if (el is ProcSeperator) {

                }
            }
            try{
                returnValue = double.Parse(groupCanvas.GetValue(Canvas.TopProperty).ToString());
                returnValue = double.Parse(groupCanvas.GetValue(Canvas.LeftProperty).ToString());
            } catch {}
            return returnValue;
        }


        private ConnectorPoint GetConnectorPoint(ConnectionObject conn, Point offsetsrc, Point offsetdest)
        {
            //Later on Change to Get center depending on Connection Type and Y and N connection
            ConnectorPoint result = new ConnectorPoint();
            result.SrcLeft = offsetsrc.X;
            result.SrcTop = offsetsrc.Y;
            result.DestLeft = offsetdest.X;
            result.DestTop = offsetdest.Y;
            return result;
        }

        private Canvas CrudeFindName(List<GroupObject> groups, string ProcID, out Canvas inGroupCanvas)
        {
            foreach (GroupObject group in groups)
            {
                Canvas groupCanvas = StackMain.FindName(DiagramToDraw.DiagramID + group.GroupName) as Canvas;
                if (groupCanvas == null)
                    continue;
                foreach (UIElement elm in groupCanvas.Children)
                {
                    if (elm is Canvas)
                    {
                        if ((elm as Canvas).Name == ProcID)
                        {
                            inGroupCanvas = groupCanvas;
                            return(Canvas)elm;
                        }

                    }
                }
            }
            inGroupCanvas = null;
            return null;
        }


        public class ProcedureClickedArgs : EventArgs {
            private readonly ProcedureObject m_proc;
            private readonly DiagramObject m_diag;



            public ProcedureClickedArgs(DiagramObject diagram, ProcedureObject procedure) {
                m_diag = diagram;
                m_proc = procedure;
            }

            public ProcedureObject Procedure {
                get { return m_proc; }
            }

            public DiagramObject Diagram {
                get { return m_diag; }
            }
        }

        private Line GetVerticalLine(double X1, double Y1, double Y2,string ColorCode)
        {
            Line result = new Line();
            result.Stroke = new SolidColorBrush(ColorHelper.GetColor("#FF" + ColorCode));
            result.StrokeThickness = 3;
            result.X1 = X1;
            result.X2 = result.X1;
            result.Y1 = Y1;
            result.Y2 = Y2;
            return result;
        }

        private Line GetHorizontalLine(double X1, double X2, double Y1,string ColorCode)
        {
            Line result = new Line();
            result.Stroke = new SolidColorBrush(ColorHelper.GetColor(ColorCode));
            result.Stroke = new SolidColorBrush(Colors.Green);
            result.StrokeThickness = 3;
            result.X1 = X1;
            result.X2 = X2;
            result.Y1 = Y1;
            result.Y2 = result.Y1;
            return result;
        }


    }


    public class ProcSeperator
    {
        public string GroupName;
        public double currentTop;
        public double currentLeft;
        public double usedrow = 0;
    }

    public class HeightRef
    {
        public int Count;
        public string Name;
    }

    public enum RelativeToSourceSide
    {
        Right = 0,
        Left = 1,
        SameLine = 2
    }

    public enum RelativeToSourceLevel
    {
        Top = 1,
        Bottom = 0,
        SameLevel = 2
    }
}
